using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;

namespace WindowsApplicationLZ77
{
    /// <summary>
    /// Singleton.
    /// Representa las Tablas que son necesarias para codificar en bits las Posiciones y Longitudes
    /// generadas por el algoritmos de compresion.
    /// </summary>
    class Tabla
    {
        private static Tabla instance;

        //Tablas, Contienen elementos de tipos: (int,String)
        //El String es la representacion de los bits a generar y el int su valor.
        //Ejemplo: (2,"010") para la tabla_3_bits
        private System.Collections.Hashtable _tablaLongitud;
        private int _cantBitsTablaLongitud;
        
        //Idem para la tabla de posiciones
        private System.Collections.Hashtable _tablaPosicion;
        private int _cantBitsTablaPosicion;

        private Tabla(int cantCaracteres,int longVentanaMemoria)
        {
            //Tabla Posiciones
            _tablaPosicion = new Hashtable(longVentanaMemoria);
            _cantBitsTablaPosicion = (int)(Math.Log(longVentanaMemoria, 2));

            //Tabla Longitudes
            //Funcion Techo de log en base 2 de (cantCaracteres)
            _cantBitsTablaLongitud = (int) (Math.Log(cantCaracteres, 2) + 1);
            if (_cantBitsTablaLongitud == 2)
            {
                _tablaLongitud = new System.Collections.Hashtable(4);
            }

            else if (_cantBitsTablaLongitud == 3)
            {
                _tablaLongitud = new System.Collections.Hashtable(8);
            }

            else if (_cantBitsTablaLongitud == 4)
            {
                _tablaLongitud = new System.Collections.Hashtable(16);
            }

            InicializarTablas();

            ValidarTabla();
        }

        #region Inicializar Tablas

        private void InicializarTablas()
        {
            switch (_cantBitsTablaPosicion)
            {
                case 2:
                    _tablaLongitud.Add((int)0, "00");
                    _tablaLongitud.Add((int)1, "01");
                    _tablaLongitud.Add((int)2, "10");
                    _tablaLongitud.Add((int)3, "11");
                    break;

                case 3:
                    _tablaLongitud.Add((int)0, "000");
                    _tablaLongitud.Add((int)1, "001");
                    _tablaLongitud.Add((int)2, "010");
                    _tablaLongitud.Add((int)3, "011");
                    _tablaLongitud.Add((int)4, "100");
                    _tablaLongitud.Add((int)5, "101");
                    _tablaLongitud.Add((int)6, "110");
                    _tablaLongitud.Add((int)7, "111");
                    break;

                case 4:
                    _tablaLongitud.Add((int)0, "0000");
                    _tablaLongitud.Add((int)1, "0001");
                    _tablaLongitud.Add((int)2, "0010");
                    _tablaLongitud.Add((int)3, "0011");
                    _tablaLongitud.Add((int)4, "0100");
                    _tablaLongitud.Add((int)5, "0101");
                    _tablaLongitud.Add((int)6, "0110");
                    _tablaLongitud.Add((int)7, "0111");
                    _tablaLongitud.Add((int)8, "1000");
                    _tablaLongitud.Add((int)9, "1001");
                    _tablaLongitud.Add((int)10, "1010");
                    _tablaLongitud.Add((int)11, "1011");
                    _tablaLongitud.Add((int)12, "1100");
                    _tablaLongitud.Add((int)13, "1101");
                    _tablaLongitud.Add((int)14, "1110");
                    _tablaLongitud.Add((int)15, "1111");
                    break;
            }




            /*
             tabla_2_bits.Add("00", (int) 0);
             tabla_2_bits.Add("01", (int)1);
             tabla_2_bits.Add("10", (int)2);
             tabla_2_bits.Add("11", (int)3);

             tabla_3_bits.Add("000", (int)0);
             tabla_3_bits.Add("001", (int)1);
             tabla_3_bits.Add("010", (int)2);
             tabla_3_bits.Add("011", (int)3);
             tabla_3_bits.Add("100", (int)4);
             tabla_3_bits.Add("101", (int)5);
             tabla_3_bits.Add("110", (int)6);
             tabla_3_bits.Add("111", (int)7);

             tabla_4_bits.Add("0000", (int) 0);
             tabla_4_bits.Add("0001", (int) 1);
             tabla_4_bits.Add("0010", (int)2);
             tabla_4_bits.Add("0011", (int)3);
             tabla_4_bits.Add("0100", (int)4);
             tabla_4_bits.Add("0101", (int) 5);
             tabla_4_bits.Add("0110", (int) 6);
             tabla_4_bits.Add("0111", (int) 7);
             tabla_4_bits.Add("1000", (int) 8);
             tabla_4_bits.Add("1001", (int) 9);
             tabla_4_bits.Add("1010", (int) 10);
             tabla_4_bits.Add("1011", (int) 11);
             tabla_4_bits.Add("1100", (int) 12);
             tabla_4_bits.Add("1101", (int) 13);
             tabla_4_bits.Add("1110", (int) 14);
             tabla_4_bits.Add("1111", (int) 15);
             */
        }

        private void ValidarTabla()
        {
            bool ok = true; 

            String representacion = "";
            int valor = 0;
            
            IDictionaryEnumerator e = _tablaLongitud.GetEnumerator();
            while( e.MoveNext() && ok )
            {
                
                valor = (int) e.Key;
                representacion = (string)e.Value;
                              
                ok = ok && (representacion.Length == _cantBitsTablaPosicion) && ( Getint(representacion) == valor );

                System.Console.WriteLine(e.ToString() + " - " + representacion + " | " + valor.ToString());
            }

            if (ok == false)
            {
                System.Console.WriteLine("Tabla Invalida: Elemento: " + representacion + " | " + valor.ToString() );
                throw new Exception("Tabla Invalida: Elemento: " + representacion + " | " + valor.ToString()); 
            }
        }

        private int Getint(string representacion)
        {
            int u = 0;

            int p = representacion.Length - 1;
            for (int i = 0; i < representacion.Length; i++)
            {
                if (representacion[i] == '1')
                {
                    u = u + (int)Math.Pow(2, p);
                }

                p--;
            }

            return u;
        }

        #endregion

        public static Tabla GetInstance(int cantCaracteres,int longVentanaMemoria)
        {
            if (instance == null)
                instance = new Tabla(cantCaracteres,longVentanaMemoria);

            return instance;
        }

        public string GetCodigo(int valor) 
        {
            return (string) _tablaLongitud[ valor ]; 
        }

    }
}
